/* =========================================================================
 *
 *   The traffic processor.
 *
 *   Development by Sedelnikov Alexander (Bigking).
 *
 * =========================================================================
 */

#include "out.h"
#include "log.h"
#ifdef _WIN32
#include <windows.h>
#endif


//-----------------------------------------------------------------------//
void cout_info::format_traffic( const trf_gth    &trf,
                                const time_stamp &ts,
                                const char *format,
                                char *str_ptr,
                                int   max_len )
{
    if( max_len > 512 )
        max_len = 512;

    enum
    {
        ID_IP = 0,
        ID_VOLUME_IN,
        ID_VOLUME_OUT,
        ID_TRAFFIC_ID,
        ID_TIMESTAMP,
    };

    const char *token[] =
    {
        "ip",
        "vi",
        "vo",
        "ti",
        "ts",
        NULL,
    };

    char  buffer[ 512 + 1 ], text[ 256 ], len;
    int   token_count,
          fmt_len = strlen( format ),
          j;

    for( token_count = 0; token[ token_count ] != NULL; token_count++ );

    str_ptr[ 0 ] = 0;
    j            = 0;

    for( int i = 0; i < fmt_len, j < max_len; )
    {
        len          = -1;

        for( int tw = 0; tw < token_count; tw++ )
        {

            if( i + 2 < fmt_len &&
                format[ i ] == '%' &&
                //===//
                token [ tw ][ 0 ] ==
                format[ i + 1 ] &&
                //===//
                token [ tw ][ 1 ] ==
                format[ i + 2 ] )
            {
                len          = 0;
                str_ptr[ j ] = 0;

                switch( tw )
                {
                    case ID_IP:
                        len = sprintf( str_ptr + j,
                           "%ld.%ld.%ld.%ld",
                           ( (trf.ip       ) & 0xff ),
                           ( (trf.ip >> 8  ) & 0xff ),
                           ( (trf.ip >> 16 ) & 0xff ),
                           ( (trf.ip >> 24 ) & 0xff ) );
                    break;
                    case ID_VOLUME_IN:
                        len = sprintf( str_ptr + j,
#ifdef _WIN32
                                  "%I64u",
#else
                                  "%lld",
#endif
                                  trf.volume_in );
                    break;
                    case ID_VOLUME_OUT:
                        len = sprintf( str_ptr + j,
#ifdef _WIN32
                                  "%I64u",
#else
                                  "%lld",
#endif
                                  trf.volume_out );
                    break;
                    case ID_TRAFFIC_ID:
                        len = sprintf( str_ptr + j,
                                  "%s",
                                  cconfig::get_class_identifier( trf.tc_id ) );
                    break;
                    case ID_TIMESTAMP:
                        len = sprintf( str_ptr + j,
                                  "%04ld-%02ld-%02ld %02d:%02d:%02d",
                                  ts.year,
                                  ts.month,
                                  ts.day,
                                  ts.hour,
                                  ts.minute,
                                  0 );
                    break;
                }

                j += len;
                i += 3;

            }
        }

        if( len < 0 && j < max_len )
            str_ptr[ j++ ] = format[ i++ ];
    }

    if( j < max_len )
        str_ptr[ j ] = 0;
    else
        str_ptr[ max_len - 1 ] = 0;
}


//-----------------------------------------------------------------------//
cout_file::cout_file()
{
    fp = NULL;
}


//-----------------------------------------------------------------------//
cout_file::~cout_file()
{
}


//-----------------------------------------------------------------------//
int  cout_file::exec( const char *str )
{
    return 0;
}


//-----------------------------------------------------------------------//
void cout_file::start()
{
    // making safe name
    char     full_path_data     [ 2048 ],
             full_path_processed[ 2048 ];

    unical_name[ 0 ] = 0;

    while( true )
    {
        sprintf( full_path_data, "%s/%s%s.data",
                 cconfig::get_out_folder(),
                 label, unical_name );

        sprintf( full_path_processed, "%s/%s%s.data.processed",
                 cconfig::get_out_folder(),
                 label, unical_name );

        fp = fopen( full_path_data, "rb" );

        if( fp != NULL )
        {
            fclose( fp );

            fp = fopen( full_path_processed, "rb" );
            if( fp == NULL )
                break;

            sprintf( unical_name, "_%04X%04X%04X",
                     rand(), rand(), rand() );

            logdbg( "[%s] out file duplication was founded!",
                    full_path_processed );

            fclose( fp );
        }
        else
            break;
    }

    fp = fopen( full_path_data, "wb" );

    if( fp == NULL )
        THROW( e_start, "Can't write [%s] file!", full_path_data );
}


//-----------------------------------------------------------------------//
void cout_file::send_line( const char *str, int length )
{
    fprintf( fp, "%s\n", str );
}


//-----------------------------------------------------------------------//
void cout_file::end( ctransaction *trans )
{
    if( fp != NULL )
    {
        fflush( fp );
        fclose( fp );
        fp = NULL;
    }
}


//-----------------------------------------------------------------------//
void cout_file::finish()
{
    char     full_path[ 2048 ];

    sprintf( full_path, "%s/%s%s.data.processed",
             cconfig::get_out_folder(),
             label,
             unical_name );

    fp = fopen( full_path, "wb" );

    if( fp == NULL )
        THROW( e_end, "Can't made processed file [%s]", full_path );

    fflush( fp );
    fclose( fp );

    fp = NULL;
}



//-----------------------------------------------------------------------//
//-----------------------------------------------------------------------//
//-----------------------------------------------------------------------//
//-----------------------------------------------------------------------//
//#ifdef POSTGRES

// @fixme @todo Memory leaks [res] \

//#endif


//-----------------------------------------------------------------------//
cout_postgres::cout_postgres()
{
}


//-----------------------------------------------------------------------//
cout_postgres::~cout_postgres()
{
}


//-----------------------------------------------------------------------//
int  cout_postgres::exec( const char *str )
{
#ifndef POSTGRES
    THROW( e_start, "This application is not compiled with Postgres!" );
#else

    PGresult      *res = NULL;

//    puts( str );
    res = PQexec( pgsql, str );
    PGSQL_CHECK( res, "Can't exec query!" );
    PQclear( res );
#endif
    return 0;
}


//-----------------------------------------------------------------------//
void cout_postgres::start()
{
#ifndef POSTGRES
    THROW( e_start, "This application is not compiled with Postgres!" );
#else
    pgsql = PQconnectdb( cconfig::get_out_server() );
    if( pgsql == NULL || PQstatus( pgsql ) == CONNECTION_BAD )
    {
        if( pgsql )
            PQfinish( pgsql );
        pgsql = NULL;

        THROW( e_start, "Can't connect to the database!" );
    }

    // BEGIN;
    PGresult      *res = NULL;

    res = PQexec( pgsql, "BEGIN WORK;" );
    PGSQL_CHECK( res, "Can't start postgres transaction!" );
    PQclear( res );

#endif
}


//-----------------------------------------------------------------------//
void cout_postgres::send_line( const char *str, int length )
{
#ifndef POSTGRES
    THROW( e_send_line, "This application is not compiled with Postgres!" );
#else

    // @todo This releasation is too slow. Make prepare cmd.
    PGresult      *res = NULL;

//    puts( str );
    int   tries = 6;

    while( tries > 1 )
    {
        res = PQexec( pgsql, str );

        if( res == NULL || PQresultStatus( res ) == PGRES_FATAL_ERROR )
        {
            THROW( e_start, "Can't execute query. But PGRES_FATAL_ERROR! [%s]", res == NULL ? "NULL" : PQresultErrorMessage( res ) );
        }
        if( PQresultStatus( res ) == PGRES_NONFATAL_ERROR )
        {
            if( tries == 1 )
            {
                THROW( e_start, "Can't execute query after 6 tries [%s]", PQresultErrorMessage( res ) );
            }
            logwrn( "PGRES_NONFATAL_ERROR [%s] [6/%ld]...", PQresultErrorMessage( res ), tries );
#ifdef _WIN32
            Sleep( rand( ) % 50 + 50 );
#else
            usleep( (rand( ) % 50 + 50)*1000 );
#endif
        }
        else
            tries = 1;

        PQclear( res );

        tries--;
    }

#endif
}


//-----------------------------------------------------------------------//
void cout_postgres::end( ctransaction *trans )
{
#ifndef POSTGRES
    THROW( e_end, "This application is not compiled with Postgres!" );
#else
    PGresult      *res = NULL;

    // INSERT TO THE TRANSACTION TABLE;
    char       text[ 256 ];
    sprintf( text, "INSERT INTO %s( ident ) VALUES( '%s' );",
             cconfig::get_transaction_table(),
             trans->get_unical_id() );

    res = PQexec( pgsql, text );
    PGSQL_CHECK( res, "Can't finish postgres transaction! ( Insert to the transaction table. )" );
    PQclear( res );
    // END;

    res = PQexec( pgsql, "END WORK;" );
    PGSQL_CHECK( res, "Can't finish postgres transaction!" );
    PQclear( res );

    if( pgsql )
        PQfinish( pgsql );
    pgsql = NULL;
#endif
}


//-----------------------------------------------------------------------//
void cout_postgres::finish()
{
}
